package com.derbysoft.ota.application.translator.rs;

import com.derbysoft.modules.eip.translator.Translator;
import com.derbysoft.ota.application.util.ParagraphTypes;
import com.derbysoft.ota.interfaces.protocol._2009a.AmountPercentType;
import com.derbysoft.ota.interfaces.protocol._2009a.AmountPercentType.BasisTypeEnum;
import com.derbysoft.ota.interfaces.protocol._2009a.CancelPenaltiesType;
import com.derbysoft.ota.interfaces.protocol._2009a.CancelPenaltyType;
import com.derbysoft.ota.interfaces.protocol._2009a.CancelPenaltyType.Deadline;
import com.derbysoft.ota.interfaces.protocol._2009a.CancelPenaltyType.Deadline.OffsetDropTimeEnum;
import com.derbysoft.ota.interfaces.protocol._2009a.TimeUnitType;
import com.derbysoft.remote.dswitch.generic.constants.CancelFeeType;
import com.derbysoft.remote.dswitch.generic.constants.CancelTimeType;
import com.derbysoft.remote.dswitch.hotel.dto.CancelPenaltyDTO;
import com.derbysoft.remote.dswitch.hotel.dto.CancelPolicyDTO;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.googlecode.functionalcollections.FunctionalIterables;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import java.math.BigDecimal;
import java.math.BigInteger;

import static com.derbysoft.remote.dswitch.hotel.constants.CancelPenaltyType.NON_PENALTY;
import static com.derbysoft.remote.dswitch.hotel.constants.CancelPenaltyType.UNKNOWN;

public class CancelPenaltiesTranslator implements Translator<CancelPolicyDTO, CancelPenaltiesType> {

    private static Logger logger = LoggerFactory.getLogger(CancelPenaltiesTranslator.class);
    private static final long ONE_HUNDRED = 100L;

    @Override
    public CancelPenaltiesType translate(final CancelPolicyDTO source) {

        if (NON_PENALTY == source.getCancelPenaltyType() || UNKNOWN == source.getCancelPenaltyType()) {
            return null;
        }

        if (CollectionUtils.isEmpty(source.getPenalties())) {
            return null;
        }

        CancelPenaltiesType dest = new CancelPenaltiesType();
        //deadline date is arrival date
        //only support before_deadline, after_deadline and no_show penalty should be set into guarantee info?
        dest.getCancelPenalty().addAll(FunctionalIterables.make(source.getPenalties()).filter(
                new Predicate<CancelPenaltyDTO>() {
                    public boolean apply(@Nullable CancelPenaltyDTO input) {
                        return CancelTimeType.BEFORE_DEADLINE == input.getCancelTimeType();
                    }
                }).map(
                new Function<CancelPenaltyDTO, CancelPenaltyType>() {
                    public CancelPenaltyType apply(CancelPenaltyDTO from) {
                        CancelPenaltyType cancelPenaltyType = new CancelPenaltyType();
                        cancelPenaltyType.setDeadline(deadline(source, from));
                        cancelPenaltyType.getPenaltyDescription().add(ParagraphTypes.of(source.getDescription()));
                        cancelPenaltyType.setAmountPercent(amountPercent(from));
                        return cancelPenaltyType;
                    }
                }
        ).toCollection());

        return dest;
    }

    private Deadline deadline(CancelPolicyDTO source, final CancelPenaltyDTO from) {
        Deadline deadline = new Deadline();
        deadline.setAbsoluteDeadline(source.getDeadline());
        Integer dayBeforeDeadline = from.getDayBeforeDeadline();
        if (dayBeforeDeadline != null) {
            deadline.setOffsetDropTime(OffsetDropTimeEnum.BEFORE_ARRIVAL);
            deadline.setOffsetTimeUnit(TimeUnitType.DAY);
            deadline.setOffsetUnitMultiplier(dayBeforeDeadline);
        }
        return deadline;
    }


    private AmountPercentType amountPercent(CancelPenaltyDTO from) {
        AmountPercentType amountPercent = new AmountPercentType();
        if (CancelFeeType.FIXED == from.getCancelFeeType()) {
            amountPercent.setAmount(from.getValue());
        } else if (CancelFeeType.PERCENT_OF_TOTAL == from.getCancelFeeType()) {
            amountPercent.setPercent(from.getValue());
            amountPercent.setBasisType(BasisTypeEnum.FULL_STAY);
        } else if (CancelFeeType.FIRST_NIGHT == from.getCancelFeeType()) {
            amountPercent.setNmbrOfNights(BigInteger.valueOf(1));
            amountPercent.setBasisType(BasisTypeEnum.NIGHTS);
        } else if (CancelFeeType.TOTAL == from.getCancelFeeType()) {
            amountPercent.setPercent(BigDecimal.valueOf(ONE_HUNDRED));
            amountPercent.setBasisType(BasisTypeEnum.FULL_STAY);
        } else if (CancelFeeType.PERCENT_OF_FIRST_NIGHT == from.getCancelFeeType()) {
            amountPercent.setPercent(from.getValue());
            amountPercent.setBasisType(BasisTypeEnum.NIGHTS);
        } else {
            logger.warn("We have no idea on how to deal with cancel fee type of [" + from.getCancelFeeType() + "]");
        }
        return amountPercent;
    }


}
